package cn.dzz.util;

import com.zaxxer.hikari.HikariDataSource;

import javax.sql.DataSource;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author ArkD42
 * @file OA
 * @create 2020 - 06 - 17 - 22:39
 */
public class ServiceTxProxy <GenericService>{

    /**
     * 目标对象，也就是业务对象
     * 我们希望不仅仅是一个业务逻辑接口，应该整个业务逻辑接口都可以被代理
     */
    private GenericService genericService;

    /**
     * 我创建这个代理对象可以不先设置目标对象
     * 对实例重载
     */
    public ServiceTxProxy() {}

    /**
     * 创建实例时就注入目标对象
     * @param genericService
     */
    public ServiceTxProxy(GenericService genericService) {
        this.genericService = genericService;
    }

    /**
     * 调用注入方法来注入目标对象
     * @param genericService
     */
    public void setGenericService(GenericService genericService) {
        this.genericService = genericService;
    }


    /**
     * 得到代理对象，我们将由代理对象来操作业务
     * 在得到代理对象之前注入数据源，我们不希望只能使用单一的数据源
     * 业务层的调用，一定是在代理之后执行，所以在这里注入数据源不会造成空指针的问题
     * @param dataSource
     * @return
     */
    public GenericService getGenericService(DataSource dataSource) {
        // 调用代理类的创建代理实例方法
        return (GenericService) Proxy.newProxyInstance(
                // 第一参数：类加载器实例,可以反射任意实例获取
                genericService.getClass().getClassLoader(),
                // 第二参数：目标对象的接口数组，因为接口是允许多实现的
                genericService.getClass().getInterfaces(),
                // 第三参数: 调用处理器的实现实例，需要重新调用方法，对目标对象增强
                new InvocationHandler() {

                    /**
                     *
                     * @param proxy ? 代理对象？
                     * @param method
                     * @param args
                     * @return
                     * @throws Throwable
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                        // 目标对象就是我们的Service对象,在对象调用之前，开启事务
                        TransactionManager.setDataSource(dataSource);
                        TransactionManager.begin();

                        Object theGenericServiceData = null;

                        try {
                            // 目标对象的方法执行！！！
                            theGenericServiceData = method.invoke(genericService, args);

                            // 对执行完毕的Service对象 进行提交或者回滚
                            TransactionManager.commit();
                        } catch (Exception exception) {
                            exception.printStackTrace();
                            TransactionManager.rollback();
                        }
                        return theGenericServiceData;
                    }
                }
        );
    }


}
